use crate::{
    controllers::{configure_controller, default_handler},
    mdb::try_new_from_config,
    middlewares::session::CacheSession,
    models::sync_models,
    services::{configure_services, session_service::SessionService},
    tasks::start_tasks,
    utils::cache::GLOBAL_CACHE,
    AppResult, Config, GLOBAL_CONFIG,
};
use actix_files::Files;
use actix_web::{
    middleware::{Logger, NormalizePath},
    web, App, HttpServer,
};
use std::sync::Arc;
use tokio::fs::create_dir_all;

#[derive(Debug, Default)]
pub struct Application {
    pub(crate) config_path: Option<String>,
}

impl Application {
    pub fn new() -> Self {
        Self { config_path: None }
    }
    pub fn set_config_path(self, config_path: Option<String>) -> Self {
        Self { config_path }
    }
    pub async fn start(self) -> AppResult<()> {
        let config_path = self.config_path.expect("配置文件路径不能为空");
        let config = Config::try_from_file(config_path.clone()).map_err(|err| {
            log::error!(
                "配置文件读取失败，请检查配置文件\"{}\"是否处于项目目录下",
                config_path
            );
            err
        })?;

        // 创建缓存数据库
        let cache_db = sled::open("cache_sled")
            .expect("缓存创建失败，请检查从磁盘空间以及cache_sled是否被阻止创建");
        log::info!(
            "缓存数据库状态:\"{}\"",
            if !cache_db.was_recovered() {
                "新建"
            } else {
                "恢复"
            }
        );

        create_dir_all(&config.upload.upload_base_path).await?; // 初始化上传路径

        let config_arc = Arc::new(config); // 移入闭包中，用于初始化服务

        GLOBAL_CONFIG.write().unwrap().replace(config_arc.clone()); // 写入全局静态配置中

        GLOBAL_CACHE.write().unwrap().replace(cache_db.clone()); // 设置为全局缓存

        let (mdb, _) = try_new_from_config(&config_arc.mongo).unwrap();

        sync_models(mdb.clone(), config_arc.clone()).await?; // 同步modules

        start_tasks(cache_db.clone(), config_arc.clone()); // 启动后台任务

        let config_arc1 = config_arc.clone(); // 用于后续的bind和info
        let t = HttpServer::new(move || {
            // session 私钥创建
            let mut key = Vec::with_capacity(32);
            key.extend_from_slice(config_arc.cookie.key.as_bytes());

            if key.len() < 32 {
                // 私钥不足32字节进行补足
                key.extend(vec![0; 32 - key.len()])
            }
            let (mdb, m_client) = try_new_from_config(&config_arc.mongo).unwrap();
            let session_svr =
                SessionService::new(config_arc.clone(), mdb.clone(), m_client.clone());
            App::new()
                .service(Files::new(
                    config_arc.upload.web_base_path.as_str(),
                    config_arc.upload.upload_base_path.as_str(),
                ))
                .wrap(
                    CacheSession::new(
                        &config_arc.cookie.namespace,
                        &key,
                        cache_db.clone(),
                        session_svr,
                    )
                    .cookie_max_age(time::Duration::seconds(config_arc.cookie.max_age))
                    .cookie_http_only(config_arc.cookie.http_only)
                    .cookie_secure(config_arc.cookie.secure)
                    .cookie_name(config_arc.cookie.name.as_str()),
                )
                .wrap(NormalizePath::trim())
                .wrap(Logger::default())
                .configure(configure_controller)
                .default_service(web::route().to(default_handler))
                .configure(configure_services(
                    mdb,
                    m_client,
                    config_arc.clone(),
                    cache_db.clone(),
                ))
                .app_data(web::Data::new(cache_db.clone()))
                .app_data(web::Data::new(config_arc.clone()))
        })
        .workers(num_cpus::get())
        .bind((config_arc1.server.host.clone(), config_arc1.server.port))?
        .run();
        t.await?;
        Ok(())
    }
    pub fn get_default_config() -> Config {
        Config::default()
    }
}
